/*
 * jusb.cpp
 *
 *  Created on: 06.02.2009
 *      Author: fregat
 */

# include "jusb.h"
# include "usb_interface.h"
# include <iostream>
# include <map>

using namespace fregat::microchip::usb;
using namespace fregat::utility::logger;

static fregat::microchip::usb::jusbEnvironment usbenv;

fregat::microchip::usb::jniInterface::jniInterface(JNIEnv *penv_, jobject jo_) :
	penv(penv_), jo(jo_) {
}

void fregat::microchip::usb::jniInterface::doWriteLog(jLogger *pjl,
		logLevel_t ll, std::string ss) {
	INFO("jniInterface::doWriteLog");
	jclass clazz = penv->FindClass(pjl->getClassName().c_str());
	INFO("jniInterface::doWriteLog");
	jmethodID mid = 0;
	if (ll == logInterface::INFO)
		mid = penv->GetMethodID(clazz, pjl->getInfoMethodName().c_str(),
				"(Ljava/lang/String;)V");
	else if (ll == logInterface::DEBUG) {
		INFO("jniInterfase::doWriteLog");
		mid = penv->GetMethodID(clazz, pjl->getDebugMethodName().c_str(),
				"(Ljava/lang/String;)V");
		INFO("jniInterface::doWriteLog");
	} else if (ll == logInterface::FAULT)
		mid = penv->GetMethodID(clazz, pjl->getErrorMethodName().c_str(),
				"(Ljava/lang/String;)V");
	else {
		INFO("jniInterface::doWriteLog %d is unknown", int(mid));
		std::stringstream ss;
		ss << __FILE__ << ":" << __LINE__ << ". Loglevel " << ll
				<< " is unknown." << std::endl;
		usbenv.throwException(exceptionInterface::INVALID_LOG_LEVEL, ss.str());
	}
	if (mid == 0) {
		usbenv.throwException(exceptionInterface::INVALID_METHOD_ID,
				"Method was not found");
	}
	INFO("jniInterface::doWriteLog : mid = %d; ss.c_str = %s;", int(mid), ss.c_str());
	penv->CallObjectMethod(jo, mid, penv->NewStringUTF(ss.c_str()));
}

void fregat::microchip::usb::jniInterface::doThrowException(jException *pjex,
		exceptionInterface::exceptionID_t eid, std::string s) {
	class_name_t cn = pjex->getExceptionClassName(eid);
	std::cerr << "doThrowException: " << cn << ": " __FILE__ << "." << __LINE__
			<< std::endl;
	jclass clazz = penv->FindClass(cn.c_str());
	std::cerr << "doThrowException: " << s << ": " << __FILE__ << "."
			<< __LINE__ << std::endl;
	penv->ThrowNew(clazz, s.c_str());
}

JNIEXPORT void JNICALL
Java_fregat_microchip_usb_jusb_readConfiguration(JNIEnv *env, jobject jo) {
	INITLOG(new DefaultFactory());
	INFO("Start readConfiguration.");
	jniBindings jb(env, jo);
	try {
		jb.readConfiguration();
	} catch (devices_not_found &dnfex) {
		jb.throwException(exceptionInterface::DEVICES_NOT_FOUND, "Any devices are not found on the bus...");
	}
}

JNIEXPORT jobject JNICALL
Java_fregat_microchip_usb_jusb_findDevice(JNIEnv *env, jobject jo, jshort v,
		jshort p) {
	jint descriptor = -1;
	jobject jres = 0;
	jniBindings jb(env, jo);
	INFO("Find device mit vendor = %x; product = %x.", v, p);
	INFO("Begin to find device");
	try {
		descriptor = jb.findDevice(v, p);
		INFO("dada");
		jres = jb.createUsbDeviceObject(
				"fregat/microchip/usb/device/usb_device", descriptor);
		if (jres)
			INFO("OK.");
	} catch (device_not_found &dnfex) {
		std::stringstream ss;
		ss << "Device with vendor 0x" << std::hex << v << " and product id 0x"
				<< p << " not found" << std::dec << std::endl;
		DEBUG("Exception. %s", ss.str().c_str());
		jb.throwException(exceptionInterface::DEVICE_NOT_FOUND, ss.str());
	}
	return jres;
}

JNIEXPORT jint JNICALL
Java_fregat_microchip_usb_jusb_openDevice(JNIEnv *penv, jobject jo,
		jobject descr_) {
	jniBindings jb(penv, jo);
	int descr = int(jb.getIntField(descr_, "device_descriptor"));
	std::stringstream ss;
	ss << __FILE__ << ":" << __LINE__ << ": Descriptor " << int(descr)
			<< std::endl;
	jb.debug(ss.str());
	jb.openDevice(descr);
	return 0;
}

JNIEXPORT jint JNICALL
Java_fregat_microchip_usb_jusb_writeDevice(JNIEnv *penv, jobject jo,
		jobject descr_, jbyteArray data) {
	jniBindings jb(penv, jo);
	int descr = int(jb.getIntField(descr_, "device_descriptor"));
	std::stringstream ss;
	ss << __FILE__ << ":" << __LINE__ << ": Descriptor " << int(descr)
			<< std::endl;
	jb.debug(ss.str());
	return jb.writeDevice(descr, data);
}

JNIEXPORT jbyteArray JNICALL
Java_fregat_microchip_usb_jusb_readDevice(JNIEnv *penv, jobject jo,
		jobject descr_) {
	jniBindings jb(penv, jo);
	int descr = int(jb.getIntField(descr_, "device_descriptor"));
	return jb.readDevice(descr);
}

fregat::microchip::usb::jusbEnvironment::jusbEnvironment() :
			plogger(new jLogger("fregat/microchip/usb/jusb", "info", "debug",
					"error")) {
	pexception = new jException();
	pexception->addException(exceptionInterface::DEVICES_NOT_FOUND,
				"fregat/microchip/usb/device/devicesNotFoundException");
	pexception->addException(exceptionInterface::DEVICE_NOT_FOUND,
			"fregat/microchip/usb/device/deviceNotFoundException");
	pexception->addException(exceptionInterface::INVALID_METHOD_ID,
			"fregat/microchip/usb/invalidMethodException");
	pexception->addException(exceptionInterface::INVALID_LOG_LEVEL,
			"fregat/microchip/usb/invalidLogleveException");
	pexception->addException(exceptionInterface::CHANNEL_NOT_FOUND,
			"fregat/microchip/usb/channelNotFoundException");
	pexception->addException(exceptionInterface::DEVICE_NOT_OPENED,
			"fregat/microchip/usb/device/deviceNotOpenedException");
	pexception->addException(exceptionInterface::CHANNEL_INVALID_STATE,
			"fregat/microchip/usb/channelInvalidState");
}




fregat::microchip::usb::jniBindings::jniBindings(JNIEnv *penv, jobject jo) {

	usbenv.update(penv, jo);
}

jobject fregat::microchip::usb::jniBindings::createUsbDeviceObject(
		class_name_t cn, usb_device_descriptor_t i) {
	return usbenv.createUsbDeviceObject(cn, i);
}

void fregat::microchip::usb::jniBindings::setIntField(jobject jo,
		field_name_t fn, int value) {
	return usbenv.setIntField(jo, fn, value);
}

int fregat::microchip::usb::jniBindings::getIntField(jobject jo,
		field_name_t fn) {
	return usbenv.getIntField(jo, fn);
}

void fregat::microchip::usb::jniBindings::throwException(
		exceptionInterface::exceptionID_t eid, std::string s) {
	usbenv.throwException(eid, s);
}

void fregat::microchip::usb::jniBindings::debug(std::string s) {
	usbenv.debug(s);
}

void fregat::microchip::usb::jniBindings::error(std::string s) {
	usbenv.error(s);
}
void fregat::microchip::usb::jniBindings::info(std::string s) {
	usbenv.info(s);
}

void fregat::microchip::usb::jniBindings::readConfiguration() {
	INFO("dada");
	try {
		usbenv.readConfiguration();
	}
	catch (devices_not_found dnf) {
		INFO("dada");
		throwException(exceptionInterface::DEVICES_NOT_FOUND, dnf.to_string());
	}
}

usb_device_descriptor_t fregat::microchip::usb::jniBindings::findDevice(
		vendor_t v, productid_t p) {
	return usbenv.findDevice(v, p);
}

boost::shared_ptr<usb_channel_t> fregat::microchip::usb::jniBindings::findChannel(
		usb_device_descriptor_t d) {
	return usbenv.findChannel(d);
}

int fregat::microchip::usb::jniBindings::openDevice(usb_device_descriptor_t d) {
	try {
		return usbenv.open_device(d);
	} catch (channel_not_found &cnf) {
		throwException(exceptionInterface::CHANNEL_NOT_FOUND, cnf.to_string());
	} catch (device_not_opened &dno) {
		throwException(exceptionInterface::DEVICE_NOT_OPENED, dno.to_string());
	}
	return -1;
}

int fregat::microchip::usb::jniBindings::writeDevice(
		const usb_device_descriptor_t &d, const jbyteArray &data) {
	int len = usbenv.getByteArrayLength(data);
	char *pdata = usbenv.getByteArrayElement(data, 0);
	std::vector<unsigned char> v((unsigned char *) pdata,
			(unsigned char *) (pdata + len));
	std::stringstream ss;
	for (std::vector<unsigned char>::iterator iter = v.begin(); iter != v.end(); ++iter)
		ss << std::hex << "0x" << (short) *iter << " ";
	//for (char *iter = pdata; iter < pdata + len; ++iter)
	//	ss << *iter << " ";
	ss << std::endl;
	debug(ss.str());
	try {
		return usbenv.write_device(v, d);
	} catch (config_invalid &cinv) {
		throwException(exceptionInterface::INVALID_CONFIG, cinv.to_string());
	} catch (claim_interface_invalid &clinv) {
		throwException(exceptionInterface::DEVICE_NOT_CLAIMED,
				clinv.to_string());
	}
	return -1;
}

jbyteArray fregat::microchip::usb::jniBindings::readDevice(
		const usb_device_descriptor_t &d) {
	std::vector<unsigned char> result(USBGEN_EP_SIZE);
	size_t read_size = usbenv.read_device(result, d);
	result.resize(read_size);
	std::stringstream ss;
	ss << "Has been read " << read_size << " bytes " << std::endl;
	//debug (ss.str ());
	for (size_t i = 0; i < read_size; ++i)
		ss << std::hex << (int) result[i] << " ";
	ss << std::endl;
	debug(ss.str());
	return usbenv.getByteArray(result);
}

